Migrations 数据迁移
Sequelize v2.0中引入了一个新的CLI(命令行工具),就像使用 Git/SVN 管理代码一样,你可以使用迁移功能(Migrations)来跟踪数据库的更改。通过迁移功能,你可以将现有数据库转移到另一个状态,反之亦然。进行迁移时,状态转换会被保存到迁移文件中,这些文件描述了如何进入新状态以及如何恢复更改以恢复到旧状态。
- CLI
1.1 CLI安装
1.2 引导
1.3 创建第一个模型(并迁移)
1.4 执行迁移
1.5 撤消迁移
1.6 创建第一个种子
1.7 执行种子
1.8 撤消种子
1.9 CLI使用帮助
- 高级主题
2.1 迁移结构
2.2 .sequelizerc文件
2.3 动态配置
2.4 使用环境变量
2.5 指定方言选项
2.6 生产用途
2.7 存储
2.8 配置连接字符串
2.9 使用SSL连接
2.10 程序化使用
- queryInterface对象及功能
迁移需要使用Sequelize CLI,CLI提供了对迁移和项目引导的支持。
- CLI
1.1 CLI安装
要使用CLI首先要安装相应的包:
$ npm install --save sequelize-cli
当通过-g参数全局安装npm包时,可以在命令行中通过sequelize [command]命令来使用CLI。而没有使全局安装时,就可以通过node_modules/.bin/sequelize [command]来使用CLI。
1.2 引导
在上一步中,我们创建了一个空项目,这里需要执行init命令:
$ node_modules/.bin/sequelize init
以上命令会创建以下文件夹:
config:包含配置文件,这些文件会告诉CLI怎样连接数据库
models:包含项目中的所有模型(Model)
migrations:包含所有迁移文件
seeders:包含所有种子文件
注意:以上命令使用到sequelize模块,如果你项目中没有请使用npm命令安装;另外,还需要安装对应的方言模块,以实现底层的数据库操作,如:使用MySQL数据库时就需要安装mysql2模块。
配置
继续后续操作前,我们需要使CLI能够连接到数据库,可以在config/config.json文件中配置数据库。该文件类似如下:
{ "development": { "username": "root", "password": null, "database": "database_development", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "root", "password": null, "database": "database_test", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": null, "database": "database_production", "host": "127.0.0.1", "dialect": "mysql" } }
编辑该文件,正确设置数据库的验证信息和方言(数据库类型)。
注意:如果你配置的数据库还不存在,调用db:create命令即可自动创建。
1.3 创建第一个模型(并迁移)
正确配置CLI的配置文件后,就可以创建你的第一个迁移。可以通过一个命令来轻松的完成这一操作。
创建模型使用model:generate命令,该命令包含以下两个参数:
name-模型名
attributes-模型属性列表
接下来我们来创建一个名为User的模型:
node_modules/.bin/sequelize model:generate --name User --attributes firstName:string,lastName:string,email:string
以上命令会做以下两件事:
在models文件夹下创建模型文件user
在migrations文件夹下创建名称类似XXXXXXXXXXXXXX-create-user.js的迁移文件
注意:Sequelize只会使用模型文件,模型文件是对数据库中表的表示;迁移文件是对该模型的更改,或者说是CLI所要使用的表;而“迁移”可以认为对数据库修改的一次提交或日志。
1.4 执行迁移
到目前为目,我们尚未在数据库中插入任何内容。刚刚我们为第一个模型User创建了所需的模型和迁移文件。
现在要在数据库中创建该表,这时需要执行db:migrate命令:
$ node_modules/.bin/sequelize db:migrate
以上操作会执行以下步骤:
确保数据库中有一个名为SequelizeMeta的表,此表用于记录在当前数据库上运行的迁移
查找尚示执行的迁移文件,这一步通过SequelizeMeta表来实现。在本例中,将执行上一步中创建的XXXXXXXXXXXXXX-create-user.js迁移文件。
创建一个名为Users的表,其中包含迁移文件中指定的所有列。
1.5 撤消迁移
现在我们的表已在数据库中创建并保存。通过迁移功能,只需运行命令即可恢复到旧状态。
撤消迁移可以使用db:migrate:undo命令,此命令将还原到最近的迁移:
$ node_modules/.bin/sequelize db:migrate:undo
此外,可以通过db:migrate:undo:all命令来撤消所有迁移来恢复到初始状态。还可以通过--to选项来传递迁移文件名称,以恢复到特定迁移。
$ node_modules/.bin/sequelize db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
1.6 创建第一个种子
有些情况下我们可能需要在一个表中插入一些默认数据。如:在前面的User表中创建一个演示用户。
要管理所有数据迁移,可以使用seeders。种子文件表示数据的一些变化,可用于使用样本数据或测试数据填充数据库表。
创建种子文件可以使用seed:generate命令。现在我们创建一个种子文件,它会添加一个演示用户到User表中:
$ node_modules/.bin/sequelize seed:generate --name demo-user
这个命令会在seeders文件夹中创建一个种子文件,文件名类似XXXXXXXXXXXXXX-demo-user.js。它遵循与迁移文件相同的up/down语义。
现在编辑这个文件,以添加一个演示用户到User表:
'use strict';
module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.bulkInsert('Users', [{ firstName: 'John', lastName: 'Doe', email: 'demo@demo.com' }], {}); },
down: (queryInterface, Sequelize) => { return queryInterface.bulkDelete('Users', null, {}); } };
1.7 执行种子
上一步我们创建了种子文件,但它还未提交到数据库中。这时可以通过db:seed:all命令实现:
$ node_modules/.bin/sequelize db:seed:all
以上命令会执行种子文件,演示用户也会被添加到User表中。
注意:种子执行不像迁移,它不会存储在任何位置(迁移会存储在SequelizeMeta中),如果需要存储请参阅Storage一节。
1.8 撤消种子
使用种子存储后,种子的执行可以撤消。可以通过以下两个命令实现:
db:seed:undo命令撤消最近的一次种子操作:
node_modules/.bin/sequelize db:seed:undo
db:seed:undo:all命令撤消所有种子操作:
node_modules/.bin/sequelize db:seed:undo:all
1.9 CLI使用帮助
可以通过sequelize help查看使用帮助,该命令会输出当前运行环境中的Node版本、CLI版本、ORM版本及当前版本CLI所支持的命令。
类似如下:
Sequelize CLI [Node: 10.14.2, CLI: 5.4.0, ORM: 4.42.0]
sequelize [命令]
命令: sequelize db:migrate 运行待执行的迁移 sequelize db:migrate:schema:timestamps:add 更新迁移表以获取时间戳 sequelize db:migrate:status 列出所有迁移的状态 sequelize db:migrate:undo 恢复迁移 sequelize db:migrate:undo:all 恢复所有迁移 sequelize db:seed 运行指定的种子 sequelize db:seed:undo 撤消最近执行的种子 sequelize db:seed:all 运行所有种子 sequelize db:seed:undo:all 撤消所有已执行的种子 sequelize db:create 创建配置中指定的数据库 sequelize db:drop 删除配置中指定的数据库 sequelize init 初始化项目 sequelize init:config 初始化配置 sequelize init:migrations 初始化迁移 sequelize init:models 初始化模型 sequelize init:seeders 初始化种子 sequelize migration:generate 生成新的迁移文件 [aliases: migration:create] sequelize model:generate 生成一个模模型及期迁移文件 [aliases: model:create] sequelize seed:generate 生成一个新的种子文件 [aliases: seed:create]
选项: --help 显示帮助信息 [布尔] --version 显示版本号 [布尔]
- 高级主题
2.1 迁移结构
所有迁移都在项目顶层一个名为migrations的目录中。
sequelize会生成迁移的结构,以下是一个典型的迁移文件结构:
module.exports = { up: function(queryInterface, Sequelize) { // 转换为新状态的逻辑 },
down: function(queryInterface, Sequelize) { // 恢复修改的逻辑 } }
通过queryInterface对象可用来修改数据库。Sequelize对象中存储了数据类型,如:STRING和INTEGER。up和down函数需要返回一个Promise。以下是一些示例代码:
module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Person', { name: Sequelize.STRING, isBetaMember: { type: Sequelize.BOOLEAN, defaultValue: false, allowNull: false } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Person'); } }
可以通过queryInterface了节查看该对象所支持的方法。
2.2 .sequelizerc文件
.sequelizerc文件一个特殊的配置文件,它允许你指定通常作为参数传递给CLI的各种选项。可以使用它的一些场景:
需要重写默认的migrations, models, seeders或config文件夹。
需要重命名config.json时。如:重命名为database.json或其它。
除以上说明外,还有更多的应用场景。接下来,我们来看看如何使用这个文件来进行自定义配置。
首先,在项目根目录下创建.sequelizerc文件:
$ touch .sequelizerc
以下是一个示例配置:
const path = require('path');
module.exports = { 'config': path.resolve('config', 'database.json'), 'models-path': path.resolve('db', 'models'), 'seeders-path': path.resolve('db', 'seeders'), 'migrations-path': path.resolve('db', 'migrations') }
在这个配置中我们告诉CLI:
使用config/database.json进行配置设置
使用db/models做为模型目录
使用db/seeders做为种子目录
使用db/migrations做为迁移目录
2.3 动态配置
配置文件默认是一个名为config.json的JSON文件,但有时你想执行一些代码或访问环境变量,这些操作在JSON文件中是不可能实现的。
Sequelize CLI可以从JSON或JS文件中读取配置,这可以通过.sequelizerc文件配置。
要使用JS格式的文件做为配置文件,可以在.sequelizerc文件中配置如下:
const path = require('path');
module.exports = {
'config': path.resolve('config', 'config.js')
}
现在Sequelize CLI会加载config/config.js文件获取配置选项,这样通过这个JS文件可以执行任何代码并导出最终的动态配置文件。示例如下:
const fs = require('fs');
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: 'database_test',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOSTNAME,
dialect: 'mysql',
dialectOptions: {
ssl: {
ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
}
}
}
};
2.4 使用环境变量
使用CLI,可以直接访问config/config.js中的环境变量。
如下所示,可以在该文件中直接使用环境变量并导出:
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
dialect: 'mysql'
}
2.5 指定方言选项
有时你需要指定dialectOption选项,一般你可以在config/config.json中添加它。而当需要使用代码来获取该选项时,可以使用动态配置文件来处理。
{
"production": {
"dialect":"mysql",
"dialectOptions": {
"bigNumberStrings": true
}
}
}
备注:Sequelize中方言(dialect)选项用于指定所使用的数据库型。
2.6 生产用途
以下是一些有关在生产环境中使用CLI和迁移设置的提示。
1)使用环境变量进行配置设置,使用动态配置可以更好地实现。以下是一个在生产环境中使用安全配置的设置示例:
const fs = require('fs');
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: 'database_test',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOSTNAME,
dialect: 'mysql',
dialectOptions: {
ssl: {
ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
}
}
}
};
2.7 存储
CLI支持三种存储方式:sequelize,json,none
sequelize : 在sequelize数据库的表中存储迁移和种子
json : 在json文件中存储迁移和种子
none : 不存储任何迁移、种子
迁移存储
默认情况下,CLI会在数据库中创建一个名为SequelizeMeta的表,其中包含每个已执行迁移的条目。要更改此行为,可以将三个选项添加到配置文件中。使用,您可以选择。 如果选择json,则可以使用migrationStoragePath指定文件的路径,或者CLI将写入文件sequelize-meta.json。 如果要使用sequelize将信息保留在数据库中,但希望使用其他表,则可以使用migrationStorageTableName更改表名。
migrationStorage - 用于指定要用于存储迁移的类型,默认为:sequelize,如果使用json则需要指定migrationStoragePath
json - 使用json做为迁移存储时,指定JSON文件的路径。默认为:sequelize-meta.json
migrationStorageTableName - 使用sequelize做为迁移存储时,用于指定存储表名。默认为:migrationStorageTableName
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// 指定存储类型,默认: sequelize
"migrationStorage": "json",
// 指定存储文件名,默认: sequelize-meta.json
"migrationStoragePath": "sequelizeMeta.json",
// 指定存储表名,默认: SequelizeMeta
"migrationStorageTableName": "sequelize_meta"
}
}
备注:不建议使用none用作迁移存储。如果决定使用,应该充分考虑未记录迁移执行或未运行的影响。
种子存储
默认情况下,CLI不存储种子的执行记录。如果需要存储,可以使用seederStorage选项来配置,种子存储的各设置选项与迁移存储类似。当使用json存储时,可以使用seederStoragePath来指定路径,或者CLI使用默认的sequelize-data.json;如果需要存储在数据库中,则可以使用sequelize选项,这时可以通过seederStorageTableName来指定表名,默认将使用SequelizeData。
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// Use a different storage. Default: none
"seederStorage": "json",
// Use a different file name. Default: sequelize-data.json
"seederStoragePath": "sequelizeData.json",
// Use a different table name. Default: SequelizeData
"seederStorageTableName": "sequelize_data"
}
}
2.8 配置连接字符串
可以使用--config选项来替代数据库配置文件,可以使用--url选项传入连接字符串。例如:
$ node_modules/.bin/sequelize db:migrate --url 'mysql://root:password@mysql_host.com/database_name'
2.9 使用SSL连接
使用SSL连接时,应确保在dialectOptions和基本配置中都指定了ssl。
{
"production": {
"dialect":"postgres",
"ssl": true,
"dialectOptions": {
"ssl": true
}
}
}
2.10 程序化使用
Sequelize有一个姐妹库:umzug,可以用编程方式处理迁移任务的执行和记录。
3. queryInterface对象及功能
QueryInterface是Sequelize的内置类之一,可以通过Sequelize对象(Sequelize实例)的getQueryInterface()方法获取该类的实例(即:queryInterface对象):
var queryInterface = sequelize.getQueryInterface();
通过queryInterface对象中的方法可以对数据进行修改,其中一些方法可以修改数据库的schema,以下是该类中的部分方法说明:
createTable(tableName, attributes, options)
通过传入的表名tableName、属性attributes及其它选项options创建新表。
createTable()方法用于在数据库中创建新表。创建表时可以通过attributes属性定义一些简单或复杂的属性;而options参数,可以用于定义表所使用的编码及引擎等。
queryInterface.createTable(
'nameOfTheNewTable',
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: {
type: Sequelize.DATE
},
updatedAt: {
type: Sequelize.DATE
},
attr1: Sequelize.STRING,
attr2: Sequelize.INTEGER,
attr3: {
type: Sequelize.BOOLEAN,
defaultValue: false,
allowNull: false
},
//foreign key usage
attr4: {
type: Sequelize.INTEGER,
references: {
model: 'another_table_name',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
},
{
engine: 'MYISAM', // default: 'InnoDB'
charset: 'latin1' // default: null
}
)
dropTable(tableName, options)
删除已存在的表
queryInterface.dropTable('nameOfTheExistingTable')
dropAllTables(options)
删除数据库中所有已存在的表
queryInterface.dropAllTables()
renameTable(before, after, options)
重命名已存在的表
queryInterface.renameTable('Person', 'User')
showAllTables(options)
返回数据库中所有已存在的表的表名
queryInterface.showAllTables().then(function(tableNames) {})
describeTable(tableName, options)
返回表描述,即:返回的一个包含所有属性信息的哈希表
queryInterface.describeTable('Person').then(function(attributes) {
/*
attributes 结构类似如下:
{
name: {
type: 'VARCHAR(255)', // this will be 'CHARACTER VARYING' for pg!
allowNull: true,
defaultValue: null
},
isBetaMember: {
type: 'TINYINT(1)', // this will be 'BOOLEAN' for pg!
allowNull: false,
defaultValue: false
}
}
*/
})
addColumn(tableName, attributeName, dataTypeOrOptions, options)
向已存在的表中添加新列,数据类型可以简单或详细定义
queryInterface.addColumn(
'nameOfAnExistingTable',
'nameOfTheNewAttribute',
Sequelize.STRING
)
// or
queryInterface.addColumn(
'nameOfAnExistingTable',
'nameOfTheNewAttribute',
{
type: Sequelize.STRING,
allowNull: false
}
)
// or with an explicit schema:
queryInterface.addColumn({
tableName: 'Person',
schema: 'public'
},
'signature',
Sequelize.STRING
)
removeColumn(tableName, attributeName, options)
移除已存在的表中指定的列
queryInterface.removeColumn('Person', 'signature')
// or with an explicit schema:
queryInterface.removeColumn({
tableName: 'Person',
schema: 'public'
}, 'signature');
changeColumn(tableName, attributeName, dataTypeOrOptions, options)
修改属性的元数据。可以修改默认值、是否允许空或数据类型,修改时应该确保完全描述了新的数据类型。
queryInterface.changeColumn(
'nameOfAnExistingTable',
'nameOfAnExistingAttribute',
{
type: Sequelize.FLOAT,
allowNull: false,
defaultValue: 0.0
}
)
renameColumn(tableName, attrNameBefore, attrNameAfter, options)
重命令属性(修改列名)
queryInterface.renameColumn('Person', 'signature', 'sig')
addIndex(tableName, attributes, options)
通过指定的属性向表中索引。当没有传入options时,会自动创建索引名。
// This example will create the index person_firstname_lastname
queryInterface.addIndex('Person', ['firstname', 'lastname'])
// This example will create a unique index with the name SuperDuperIndex using the optional 'options' field.
// Possible options:
// - indicesType: UNIQUE|FULLTEXT|SPATIAL
// - indexName: The name of the index. Default is __
// - parser: For FULLTEXT columns set your parser
// - indexType: Set a type for the index, e.g. BTREE. See the documentation of the used dialect
// - logging: A function that receives the sql query, e.g. console.log
// - where: A hash of attributes to limit your index(Filtered Indexes - MSSQL & PostgreSQL only)
queryInterface.addIndex(
'Person',
['firstname', 'lastname'],
{
indexName: 'SuperDuperIndex',
indicesType: 'UNIQUE'
}
)
queryInterface.addIndex(
'Person',
['firstname', 'lastname'],
{
where: {
lastname: {
$ne: null
}
}
}
)
removeIndex(tableName, indexNameOrAttributes, options)
移除表中已存在的索引
queryInterface.removeIndex('Person', 'SuperDuperIndex')
// or
queryInterface.removeIndex('Person', ['firstname', 'lastname'])
addConstraint(tableName, attributes, options)
V4.0.0+
添加一个新约束。
tableName - 要添加约束的表的表名
attributes - 应用约束的列名数组
options - 一个包含约束定义的对象,如:约束名、类型等
options中可用的选项有:
type - 约束类型。可用的约束类型有:
UNIQUE
DEFAULT (MSSQL only)
CHECK (MySQL - Ignored by the database engine )
FOREIGN KEY
PRIMARY KEY
name - 约束名。如果不指定,sequelize将根据约束类型、表名、列名自动命名
defaultValue - 约束默认值
where - 约束检查条件
references - Object。指定目标表、列名以创建外键约束
references.table - 目标表名或表
references.field - 目录列名 Available constraints:
//UNIQUE
queryInterface.addConstraint('Users', ['email'], {
type: 'unique',
name: 'custom_unique_constraint_name'
});
//CHECK
queryInterface.addConstraint('Users', ['roles'], {
type: 'check',
where: {
roles: ['user', 'admin', 'moderator', 'guest']
}
});
//Default - MSSQL only
queryInterface.addConstraint('Users', ['roles'], {
type: 'default',
defaultValue: 'guest'
});
//Primary Key
queryInterface.addConstraint('Users', ['username'], {
type: 'primary key',
name: 'custom_primary_constraint_name'
});
//Foreign Key
queryInterface.addConstraint('Posts', ['username'], {
type: 'FOREIGN KEY',
references: { //Required field
table: 'target_table_name',
field: 'target_column_name'
},
onDelete: 'cascade',
onUpdate: 'cascade'
});
removeConstraint(tableName, constraintName, options)
V4.0.0+
移除表中已经存在的约束
queryInterface.removeConstraint('Users', 'my_constraint_name');
showConstraint(tableName, options)
返回表中已经存在的约束列表
V4.0.0+
queryInterface.showConstraint('Users');
// Returns array of objects/constraints
变更记录
[2017-06-10] 基于v4.1.0首次发布
[2019-01-21] 基于v4.42.0更新